/*     */ package com.zimbra.cs.datasource.imap;
/*     */ 
/*     */ import com.zimbra.common.service.RemoteServiceException;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.StringUtil;
/*     */ import com.zimbra.common.util.SystemUtil;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.account.DataSource;
/*     */ import com.zimbra.cs.datasource.DataSourceManager;
/*     */ import com.zimbra.cs.datasource.IOExceptionHandler;
/*     */ import com.zimbra.cs.datasource.MailItemImport;
/*     */ import com.zimbra.cs.datasource.SyncUtil;
/*     */ import com.zimbra.cs.mailbox.Flag.FlagInfo;
/*     */ import com.zimbra.cs.mailbox.Folder;
/*     */ import com.zimbra.cs.mailbox.MailItem.Type;
/*     */ import com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException;
/*     */ import com.zimbra.cs.mailbox.Mailbox;
/*     */ import com.zimbra.cs.mailclient.CommandFailedException;
/*     */ import com.zimbra.cs.mailclient.auth.Authenticator;
/*     */ import com.zimbra.cs.mailclient.imap.ImapConnection;
/*     */ import com.zimbra.cs.mailclient.imap.ListData;
/*     */ import java.io.IOException;
/*     */ import java.util.ArrayList;
/*     */ import java.util.Collections;
/*     */ import java.util.HashSet;
/*     */ import java.util.LinkedHashMap;
/*     */ import java.util.List;
/*     */ import java.util.Map;
/*     */ import java.util.Set;
/*     */ import java.util.regex.Matcher;
/*     */ import java.util.regex.Pattern;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class ImapSync
/*     */   extends MailItemImport
/*     */ {
/*     */   private ImapConnection connection;
/*     */   private SyncState syncState;
/*     */   private Folder localRootFolder;
/*     */   private char delimiter;
/*     */   private final Map<Integer, ImapFolderSync> syncedFolders;
/*     */   private ImapFolderCollection trackedFolders;
/*     */   private boolean fullSync;
/*     */   private Authenticator authenticator;
/*     */   private boolean reuseConnections;
/*  62 */   private static final Pattern ILLEGAL_FOLDER_CHARS = Pattern.compile("[:\\*\\?\"<>\\|]");
/*  63 */   private static final Log LOG = ZimbraLog.datasource;
/*     */   
/*     */   public ImapSync(DataSource ds) throws ServiceException {
/*  66 */     super(ds);
/*  67 */     validateDataSource();
/*  68 */     this.syncState = SyncStateManager.getInstance().getOrCreateSyncState(ds);
/*  69 */     this.syncedFolders = new LinkedHashMap();
/*  70 */     this.reuseConnections = ds.isOffline();
/*     */   }
/*     */   
/*     */ 
/*     */   public synchronized void test()
/*     */     throws ServiceException
/*     */   {
/*  77 */     reset(this.dataSource.getId());
/*  78 */     connect();
/*  79 */     if (this.reuseConnections) {
/*  80 */       releaseConnection();
/*     */     } else {
/*  82 */       closeConnection();
/*     */     }
/*     */   }
/*     */   
/*     */   public static void reset(String dataSourceId) {
/*  87 */     ConnectionManager.getInstance().closeConnection(dataSourceId);
/*  88 */     SyncStateManager.getInstance().removeSyncState(dataSourceId);
/*     */   }
/*     */   
/*     */   public static boolean isSyncNeeded(DataSource ds) throws ServiceException {
/*  92 */     if ((ds.isOffline()) && (ds.getSyncFrequency() > 0L)) {
/*  93 */       SyncState ss = SyncStateManager.getInstance().getSyncState(ds);
/*  94 */       if (ss == null) {
/*  95 */         return ds.getSyncFrequency() >= 0L;
/*     */       }
/*  97 */       return ss.checkAndResetHasChanges(ds);
/*     */     }
/*  99 */     return false;
/*     */   }
/*     */   
/*     */   protected void setAuthenticator(Authenticator auth) {
/* 103 */     this.authenticator = auth;
/*     */   }
/*     */   
/*     */   protected void setReuseConnections(boolean reuseConnections) {
/* 107 */     this.reuseConnections = reuseConnections;
/*     */   }
/*     */   
/*     */   protected void connect() throws ServiceException {
/* 111 */     if (this.connection == null)
/*     */     {
/* 113 */       if (isFullSync()) {
/* 114 */         ConnectionManager.getInstance().closeConnection(this.dataSource);
/*     */       }
/* 116 */       this.connection = ConnectionManager.getInstance().openConnection(this.dataSource, this.authenticator);
/*     */     }
/*     */   }
/*     */   
/*     */   private void releaseConnection() {
/* 121 */     if (this.connection != null) {
/* 122 */       LOG.debug("Releasing connection");
/* 123 */       ConnectionManager.getInstance().releaseConnection(this.dataSource, this.connection);
/* 124 */       this.connection = null;
/*     */     }
/*     */   }
/*     */   
/*     */   private void closeConnection() {
/* 129 */     if (this.connection != null) {
/* 130 */       LOG.debug("Closing connection");
/* 131 */       this.connection.close();
/* 132 */       this.connection = null;
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */   public void importData(List<Integer> folderIds, boolean fullSync)
/*     */     throws ServiceException
/*     */   {
/* 140 */     importData(fullSync);
/*     */   }
/*     */   
/*     */   public synchronized void importData(boolean fullSync) throws ServiceException {
/* 144 */     if (this.dataSource.isOffline()) {
/* 145 */       getMailbox().beginTrackingSync();
/* 146 */       this.syncState = SyncStateManager.getInstance().getOrCreateSyncState(this.dataSource);
/* 147 */       this.syncState.resetHasChanges();
/*     */     }
/* 149 */     fullSync |= forceFullSync();
/* 150 */     this.fullSync = fullSync;
/* 151 */     Set<Integer> folderIds = null;
/* 152 */     if (!fullSync)
/*     */     {
/*     */ 
/* 155 */       folderIds = new HashSet();
/* 156 */       folderIds.add(Integer.valueOf(2));
/* 157 */       if (!this.dataSource.isSaveToSent()) {
/* 158 */         folderIds.add(Integer.valueOf(5));
/*     */       }
/*     */       
/* 161 */       MessageChanges mc = this.syncState.getInboxChanges();
/* 162 */       if ((mc != null) && (mc.hasChanges())) {
/* 163 */         folderIds.addAll(mc.getFolderIdsToSync());
/*     */       }
/*     */     }
/* 166 */     connect();
/*     */     try {
/* 168 */       syncFolders(folderIds);
/* 169 */       if (this.reuseConnections) {
/* 170 */         releaseConnection();
/*     */       }
/*     */     } catch (Exception e) {
/* 173 */       throw ServiceException.FAILURE("Folder sync failed", e);
/*     */     } finally {
/* 175 */       closeConnection();
/*     */     }
/*     */   }
/*     */   
/*     */   public ImapFolderSync getInboxFolderSync()
/*     */   {
/* 181 */     return (ImapFolderSync)this.syncedFolders.get(Integer.valueOf(2));
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   private boolean forceFullSync()
/*     */     throws ServiceException
/*     */   {
/* 189 */     if (!this.dataSource.isOffline()) {
/* 190 */       return true;
/*     */     }
/* 192 */     DataSourceManager dsm = DataSourceManager.getInstance();
/* 193 */     Folder inbox = dsm.getMailbox(this.dataSource).getFolderById(null, 2);
/* 194 */     return (dsm.isSyncEnabled(this.dataSource, inbox)) && (getFolderSyncState(inbox.getId()) == null);
/*     */   }
/*     */   
/*     */   public ImapConnection getConnection() {
/* 198 */     return this.connection;
/*     */   }
/*     */   
/*     */   public ImapFolderCollection getTrackedFolders() {
/* 202 */     return this.trackedFolders;
/*     */   }
/*     */   
/*     */   public ImapFolder createFolderTracker(int itemId, String localPath, String remotePath, long uidValidity)
/*     */     throws ServiceException
/*     */   {
/* 208 */     ImapFolder tracker = new ImapFolder(this.dataSource, itemId, remotePath, localPath, Long.valueOf(uidValidity));
/* 209 */     tracker.add();
/* 210 */     this.trackedFolders.add(tracker);
/* 211 */     removeSyncState(itemId);
/* 212 */     return tracker;
/*     */   }
/*     */   
/*     */   public void deleteFolderTracker(ImapFolder tracker) throws ServiceException {
/* 216 */     tracker.delete();
/* 217 */     this.trackedFolders.remove(tracker);
/*     */   }
/*     */   
/*     */   public ImapFolderSync getSyncedFolder(int folderId) {
/* 221 */     return (ImapFolderSync)this.syncedFolders.get(Integer.valueOf(folderId));
/*     */   }
/*     */   
/*     */   private void syncFolders(Set<Integer> folderIds) throws ServiceException, IOException
/*     */   {
/* 226 */     if ((this.dataSource.isImportOnly()) || ((this.dataSource.isOffline()) && (this.fullSync))) {
/* 227 */       SyncUtil.setSyncEnabled(this.mbox, 2, true);
/*     */     }
/* 229 */     this.localRootFolder = getMailbox().getFolderById(null, this.dataSource.getFolderId());
/* 230 */     if (!this.localRootFolder.isTagged(Flag.FlagInfo.SYNCFOLDER)) {
/* 231 */       getMailbox().alterTag(null, this.localRootFolder.getId(), MailItem.Type.FOLDER, Flag.FlagInfo.SYNCFOLDER, true, null);
/*     */     }
/* 233 */     this.trackedFolders = ImapFolder.getFolders(this.dataSource);
/* 234 */     this.delimiter = this.connection.getDelimiter();
/* 235 */     syncRemoteFolders(ImapUtil.listFolders(this.connection, "*"));
/* 236 */     if (!this.dataSource.isImportOnly()) {
/* 237 */       syncLocalFolders(getLocalFolders());
/*     */     } else {
/* 239 */       purgeLocalFolders(getLocalFolders());
/*     */     }
/* 241 */     IOExceptionHandler.getInstance().resetSyncCounter(this.mbox);
/* 242 */     syncMessages(folderIds);
/* 243 */     finishSync();
/*     */   }
/*     */   
/*     */   private List<Folder> getLocalFolders() {
/* 247 */     List<Folder> folders = this.localRootFolder.getSubfolderHierarchy();
/* 248 */     List<Folder> mailFolders = new ArrayList(folders.size());
/* 249 */     for (Folder f : folders) {
/* 250 */       if (f.getDefaultView() == MailItem.Type.MESSAGE) {
/* 251 */         mailFolders.add(f);
/*     */       }
/*     */     }
/*     */     
/*     */ 
/*     */ 
/* 257 */     Collections.reverse(mailFolders);
/* 258 */     return mailFolders;
/*     */   }
/*     */   
/*     */   public boolean isFullSync() {
/* 262 */     return this.fullSync;
/*     */   }
/*     */   
/*     */   private void syncRemoteFolders(List<ListData> folders) throws ServiceException {
/* 266 */     for (ListData ld : folders) {
/* 267 */       checkIsEnabled();
/*     */       try {
/* 269 */         ImapFolderSync ifs = new ImapFolderSync(this);
/* 270 */         ImapFolder tracker = ifs.syncFolder(ld);
/* 271 */         if (tracker != null) {
/* 272 */           this.syncedFolders.put(Integer.valueOf(tracker.getItemId()), ifs);
/*     */         }
/*     */       } catch (Exception e) {
/* 275 */         syncFailed(ld.getMailbox(), e);
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */   private void purgeLocalFolders(List<Folder> folders)
/*     */     throws ServiceException
/*     */   {
/* 284 */     for (Folder folder : folders) {
/* 285 */       checkIsEnabled();
/* 286 */       int id = folder.getId();
/* 287 */       if ((id != this.localRootFolder.getId()) && (!this.syncedFolders.containsKey(Integer.valueOf(id)))) {
/*     */         try {
/* 289 */           folder = getFolder(id);
/* 290 */           if (folder != null) {
/* 291 */             ImapFolderSync ifs = new ImapFolderSync(this);
/* 292 */             ifs.purgeLocalFolder(folder);
/*     */           }
/*     */         } catch (Exception e) {
/* 295 */           syncFailed(folder.getPath(), e);
/*     */         }
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private void syncLocalFolders(List<Folder> folders) throws ServiceException {
/* 302 */     for (Folder folder : folders) {
/* 303 */       checkIsEnabled();
/* 304 */       int id = folder.getId();
/* 305 */       if ((id != this.localRootFolder.getId()) && (!this.syncedFolders.containsKey(Integer.valueOf(id)))) {
/*     */         try {
/* 307 */           folder = getFolder(id);
/* 308 */           if (folder != null) {
/* 309 */             ImapFolderSync ifs = new ImapFolderSync(this);
/* 310 */             ImapFolder tracker = ifs.syncFolder(folder);
/* 311 */             if (tracker != null) {
/* 312 */               this.syncedFolders.put(Integer.valueOf(tracker.getItemId()), ifs);
/*     */             }
/*     */           }
/*     */         } catch (Exception e) {
/* 316 */           syncFailed(folder.getPath(), e);
/*     */         }
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private void syncMessages(Set<Integer> folderIds)
/*     */     throws ServiceException
/*     */   {
/* 325 */     for (ImapFolderSync ifs : this.syncedFolders.values()) {
/* 326 */       checkIsEnabled();
/* 327 */       LocalFolder folder = ifs.getLocalFolder();
/* 328 */       int folderId = folder.getId();
/*     */       try {
/* 330 */         if ((folderIds == null) || (folderIds.contains(Integer.valueOf(folderId)))) {
/* 331 */           ifs.syncMessages();
/*     */         }
/*     */       } catch (Exception e) {
/* 334 */         syncFailed(folder.getPath(), e);
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   public FolderSyncState getFolderSyncState(int folderId) {
/* 340 */     if (this.syncState != null) {
/* 341 */       FolderSyncState ss = this.syncState.getFolderSyncState(folderId);
/* 342 */       LOG.debug("getSyncState: fid = %d, state = %s", new Object[] { Integer.valueOf(folderId), ss });
/* 343 */       return ss;
/*     */     }
/* 345 */     return null;
/*     */   }
/*     */   
/*     */   public FolderSyncState removeSyncState(int folderId) {
/* 349 */     if (this.syncState != null) {
/* 350 */       FolderSyncState ss = this.syncState.removeFolderSyncState(folderId);
/* 351 */       LOG.debug("removeSyncState: fid = %d, state = %s", new Object[] { Integer.valueOf(folderId), ss });
/* 352 */       return ss;
/*     */     }
/* 354 */     return null;
/*     */   }
/*     */   
/*     */   public FolderSyncState putSyncState(int folderId, FolderSyncState ss) {
/* 358 */     if (this.syncState != null) {
/* 359 */       LOG.debug("putSyncState: fid = %d, state = %s", new Object[] { Integer.valueOf(folderId), ss });
/* 360 */       return this.syncState.putFolderSyncState(folderId, ss);
/*     */     }
/* 362 */     return null;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   private void finishSync()
/*     */     throws ServiceException
/*     */   {
/* 370 */     for (ImapFolderSync ifs : this.syncedFolders.values()) {
/*     */       try {
/* 372 */         ifs.finishSync();
/*     */       } catch (Exception e) {
/* 374 */         LocalFolder folder = ifs.getLocalFolder();
/* 375 */         syncFailed(folder.getPath(), e);
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   public Folder getFolder(int id) throws ServiceException {
/*     */     try {
/* 382 */       return this.localRootFolder.getMailbox().getFolderById(null, id);
/*     */     } catch (MailServiceException.NoSuchItemException e) {}
/* 384 */     return null;
/*     */   }
/*     */   
/*     */   private void syncFailed(String path, Exception e)
/*     */     throws ServiceException
/*     */   {
/* 390 */     String error = String.format("Synchronization of folder '%s' failed", new Object[] { path });
/* 391 */     LOG.error(error, e);
/* 392 */     if (!canContinue(e)) {
/* 393 */       throw ServiceException.FAILURE(error, e);
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */   private boolean canContinue(Throwable e)
/*     */   {
/* 402 */     if (!this.dataSource.isOffline())
/* 403 */       return false;
/* 404 */     if ((e instanceof RemoteServiceException))
/* 405 */       return false;
/* 406 */     if ((e instanceof ServiceException)) {
/* 407 */       Throwable cause = e.getCause();
/* 408 */       return (cause == null) || (canContinue(cause));
/*     */     }
/* 410 */     return e instanceof CommandFailedException;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   String getLocalPath(ListData ld)
/*     */     throws ServiceException
/*     */   {
/* 420 */     String remotePath = ld.getMailbox();
/* 421 */     char localDelimiter = ld.getDelimiter();
/* 422 */     String relativePath = ld.getMailbox();
/*     */     
/* 424 */     if ((localDelimiter != '/') && ((remotePath.indexOf(localDelimiter) >= 0) || (remotePath.indexOf('/') >= 0)))
/*     */     {
/*     */ 
/* 427 */       String[] parts = remotePath.split("\\" + localDelimiter);
/* 428 */       for (int i = 0; i < parts.length; i++)
/*     */       {
/* 430 */         parts[i] = parts[i].replace('/', localDelimiter);
/*     */       }
/* 432 */       relativePath = StringUtil.join("/", parts);
/*     */     }
/* 434 */     relativePath = ILLEGAL_FOLDER_CHARS.matcher(relativePath).replaceAll("_");
/*     */     
/* 436 */     if (this.dataSource.ignoreRemotePath(relativePath)) {
/* 437 */       return null;
/*     */     }
/*     */     
/* 440 */     String localPath = joinPath(this.localRootFolder.getPath(), (String)SystemUtil.coalesce(new String[] { this.dataSource.mapRemoteToLocalPath(relativePath), relativePath }));
/*     */     
/*     */ 
/* 443 */     if (isUniqueLocalPathNeeded(localPath)) {
/* 444 */       int count = 1;
/*     */       for (;;) {
/* 446 */         String path = String.format("%s-%d", new Object[] { localPath, Integer.valueOf(count++) });
/* 447 */         if (LocalFolder.fromPath(this.mbox, path) == null) {
/* 448 */           return path;
/*     */         }
/*     */       }
/*     */     }
/* 452 */     return localPath;
/*     */   }
/*     */   
/*     */   private static String removeLeadingSlashes(String path)
/*     */   {
/* 457 */     while (path.startsWith("/")) {
/* 458 */       path = path.substring(1);
/*     */     }
/* 460 */     return path;
/*     */   }
/*     */   
/*     */   private static String joinPath(String parent, String child) {
/* 464 */     child = removeLeadingSlashes(child);
/* 465 */     return parent + "/" + child;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   private boolean isUniqueLocalPathNeeded(String localPath)
/*     */     throws ServiceException
/*     */   {
/* 479 */     LocalFolder lf = LocalFolder.fromPath(this.mbox, localPath);
/* 480 */     return (lf != null) && ((this.trackedFolders.getByItemId(lf.getId()) != null) || ((lf.isSystem()) && (!lf.isKnown())));
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   String getRemotePath(Folder folder)
/*     */     throws ServiceException
/*     */   {
/* 490 */     if (!this.localRootFolder.isDescendant(folder)) {
/* 491 */       return null;
/*     */     }
/* 493 */     String imapPath = this.dataSource.mapLocalToRemotePath(folder.getPath());
/* 494 */     if (imapPath == null) {
/* 495 */       if (folder.getId() < 256) {
/* 496 */         return null;
/*     */       }
/*     */       
/* 499 */       imapPath = folder.getPath();
/*     */       
/* 501 */       String rootPath = this.localRootFolder.getPath();
/* 502 */       if (!rootPath.endsWith("/")) {
/* 503 */         rootPath = rootPath + "/";
/*     */       }
/* 505 */       if (!imapPath.startsWith(rootPath)) {
/* 506 */         return null;
/*     */       }
/* 508 */       imapPath = imapPath.substring(rootPath.length());
/*     */     }
/*     */     
/* 511 */     if ((this.delimiter != 0) && (this.delimiter != '/')) {
/* 512 */       String[] parts = imapPath.split("/");
/* 513 */       for (int i = 0; i < parts.length; i++) {
/* 514 */         parts[i] = parts[i].replace(this.delimiter, '/');
/*     */       }
/* 516 */       imapPath = StringUtil.join(String.valueOf(this.delimiter), parts);
/*     */     }
/* 518 */     return imapPath;
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/datasource/imap/ImapSync.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */